<?php
error_reporting(E_ALL^E_NOTICE);
header('Content-Type:text/html;charset=gbk');

function lg($info, $time = null, $filename = 'pingan')
{
	$time = empty($time)? time() : $time;

	$filename = app()->params['logs_root']['wxpay_worker'] . $filename . '.' . date('Ymd-a', $time);
	$handler = fopen($filename, 'a+');

	$info = (is_array($info) || is_object($info))? json_encode($info, JSON_UNESCAPED_UNICODE) : $info;
	fputs($handler, date('Y-m-d H:i:s', $time) . ': ' . $info . "\n\n");
	fclose($handler);
}

class WxpayPinganGworkerCommand extends ConsoleCommand
{
	public $cronName = 'wxpaypingangworker';

	public function addWorkerFunction($funcName, $realDo)
	{
		$this->_worker->addFunction($funcName, function($job, $outParamEntities){
			extract($outParamEntities);
			echo $funcName . ' is called at ' . date('Y-m-d H:i:s') . "\n";
			
			$workLoadArgs = json_decode($job->workload(), true);
			$data = $workLoadArgs['data'];
			$dataOri = $workLoadArgs['dataOri'];
			$sign = $workLoadArgs['sign'];
			$signverified = isset($workLoadArgs['signverified'])? $workLoadArgs['signverified'] : null;
			$sign_type = isset($data['sign_type'])? $data['sign_type'] : 'md5';
			if($sign_type != 'md5'){
				$sign_type = 'md5';
			}
			$sign_type = strtolower($sign_type);
			$gclientsBizContent = isset($workLoadArgs['gclientsBizContent'])? $workLoadArgs['gclientsBizContent'] : array();
			$data['term_id'] = strtolower($data['term_id']);
			
			$merBaseInfo = MerchantBaseInfo::model()->findByAttributes(array(
				'MERCHANTNO' => $data['merchant_id'],
				'STATUS' => MerchantBaseInfo::STATUS_NORMAL
			));
			
			$merSignInfo = MerSignInfo::model()->findByAttributes(array(
				'MERCHANTNO' => $data['merchant_id'], 
				'BIZ_CHANNEL' => $data['biz_channel'], 
			    'BIZ_CHANNELSRV' => MerSignInfo::BIZ_CHANNELSRV_PINGAN,
				'STATUS' => MerSignInfo::STATUS_NORMAL
			));
			
			if(!empty($data['term_id'])){
				if(strpos($data['term_id'], MerTerminalInfo::REQUIRED_PREFIX) === 0){
					$merTerminalInfo = MerTerminalInfo::model()->findByAttributes(array(
						'MERCHANTNO' => $data['merchant_id'],
						'TERMINALNO' => $data['term_id'],
						'STATUS' => MerTerminalInfo::STATUS_NORMAL
					));
					
					if(empty($merTerminalInfo)){
						$res = new formatResult($data);
						$res->code = app()->ErrorHelper->getWxSrvEcode('SYS.TERM_NONEXISTED');
						$res->message = app()->ErrorHelper->getMsg($res->code);
						$ret = json_encode($res->getData());
					
						return $this->_signReturn($sign_type, $ret, $merBaseInfo->TOKEN_MD5, $job);
					}
				}
			}
			
			if(empty($merBaseInfo) || empty($merSignInfo)){
				$res = new formatResult($data);
				$res->code = app()->ErrorHelper->getWxSrvEcode('SYS.MERSIGN_NONEXISTED');
				$res->message = app()->ErrorHelper->getMsg($res->code);
				$ret = json_encode($res->getData());
				
				return $this->_signReturn($sign_type, $ret, $merBaseInfo->TOKEN_MD5, $job);
			}
			
			if(empty($signverified) || $signverified != 1){
				if($sign_type == 'md5'){
					if(!SignMd5Helper::verify($dataOri, $sign, $merBaseInfo->TOKEN_MD5)){
						$res = new formatResult($data);
						$res->code = app()->ErrorHelper->getWxSrvEcode('SYS.INVALID_SIGNATURE');
						$res->message = app()->ErrorHelper->getMsg($res->code);
						$ret = json_encode($res->getData());
						return $this->_signReturn($sign_type, $ret, $merBaseInfo->TOKEN_MD5, $job);
					}
				}
			}

			app()->Pingan->initParam($merSignInfo->KEY1, $merSignInfo->KEY2, $merSignInfo->KEY4);
			
			$bizContent = $data['biz_content'];
			if(is_string($bizContent)){
				$bizContent = json_decode($bizContent, true);
			}
			if(empty($bizContent)){
				$bizContent = array();
			}
			
			$realDoRet = json_encode(
				$realDo($bizContent, $data, $dataOri, $merSignInfo), 
			    JSON_UNESCAPED_UNICODE
			);
			
			return $this->_signReturn($sign_type, $realDoRet, $merBaseInfo->TOKEN_MD5, $job);
		}, array(
			'funcName' => $funcName, 
			'realDo' => $realDo
		));
	}

	public function run($args = array())
	{
        $this->beforeRun($args);
		/******************************* Wxpay Workers ****************************/
        $this->addWorkerFunction('pingan.wx.jspay', function($bizContent, $data, $dataOri, $merSignInfo){
            $bizContentFormat = array_merge(array(
                'trade_no' => '',
                'total_amount' => '0',
                'limit_credit_pay' => '0',
                'attach' => '',
                'body' => '微信支付',
                'sub_appid' => '',
                'openid' => '',
                'sub_openid' => '',
                'mch_create_ip' => app()->params['serverIp'],
                'callback_url' => '',
                'type' => '',
                'notify_url' => '',
                'exts' => '',
                'time_start' => null,
                'time_expire' => null
            ), $bizContent);
            $bizContentFormat['trade_no'] = empty($bizContentFormat['trade_no'])? $data['merchant_id'] . time() : $bizContentFormat['trade_no'];
            $bizContentFormat['body'] = empty($bizContentFormat['body'])? '微信支付' : $bizContentFormat['body'];

            $ret = new formatResult($data);
            $unFinishOrder = TradeDetail::model()->findByAttributes(array(
                'TRADENO' => $bizContentFormat['trade_no'],
                'MERCHANTNO' => $merSignInfo->MERCHANTNO,
                'BIZ_TYPE' => TradeDetail::BIZ_TYPE_WX_JSPAY,
            ));
            if(!empty($unFinishOrder)){
                if(!empty($unFinishOrder->PAY_INFO) && $unFinishOrder->TRADE_STATUS == TradeDetail::TRADE_STATUS_STARTUP){
                    $ret->code = GworkerErrorHelper::SRV_CODE_OK;

                    //老平安渠道的商户返回的payinfo数据类型不练便，新的格式化成威富通的一样
                    if(in_array($merSignInfo->MERCHANTNO, MerSignInfo::OLD_PINGAN_MERCHANT)){
                        $pay_info = json_decode($unFinishOrder->PAY_INFO, true);
                    }else{
                        $pay_info = $unFinishOrder->PAY_INFO;
                    }
                    $ret->biz_content = array(
                        'attach' => $bizContentFormat['attach'],
                        'trade_no' => $bizContentFormat['trade_no'],
                        'total_amount' => $bizContentFormat['total_amount'],
                        'pay_info' => $pay_info,
                    );

                    return $ret->getData();
                }
            }

            $result = app()->Pingan->api('payorder', array(
                'out_no' => $bizContentFormat['trade_no'],
                'pmt_tag' => $merSignInfo->KEY3,
                'original_amount' => $bizContentFormat['total_amount'] * 100,
                'trade_amount' => $bizContentFormat['total_amount'] * 100,
                'ord_name' => $bizContentFormat['body'],
                'sub_appid' => $bizContentFormat['sub_appid'],
                'sub_openid' => $bizContentFormat['openid'],
                'JSAPI' => 1,
                'notify_url' => app()->params['wxpay']['sys_notify_baseurl'].'notify/pinganwxjspay',
            ));
            $result['func'] = 'jspay';

            if($result['errcode'] == 0){
                $wxTradeDetail = TradeDetail::model()->findByAttributes(array(
                    'TRADENO' => $bizContentFormat['trade_no'],
                    'MERCHANTNO' => $merSignInfo->MERCHANTNO,
                    'BIZ_TYPE' => TradeDetail::BIZ_TYPE_WX_JSPAY
                ));
                if(empty($wxTradeDetail)){
                    $wxTradeDetail = new TradeDetail();
                    $wxTradeDetail->TRADE_STATUS = TradeDetail::TRADE_STATUS_STARTUP;
                }

                $TradeResult = json_decode($result['data']['trade_result'], true);
                $pay_info = array(
                    'appId' => $result['data']['appId'],
                    'timeStamp' => $result['data']['timeStamp'],
                    'nonceStr' => $result['data']['nonceStr'],
                    'package' => $result['data']['package'],
                    'signType' => $result['data']['signType'],
                    'paySign' => $result['data']['paySign'],
                );

                $wxTradeDetail->MERCHANTNO = $data['merchant_id'];
                $wxTradeDetail->TERMINALNO = $data['term_id'];
                $wxTradeDetail->BIZ_CHANNEL = $data['biz_channel'];
                $wxTradeDetail->BIZ_TYPE = $data['biz_type'];
                $wxTradeDetail->CHANNEL_RATE = $merSignInfo->CHANNEL_RATE;
                $wxTradeDetail->CHANNEL_FEE = $merSignInfo->CHANNEL_RATE * 0.01 * $bizContentFormat['total_amount'];
                $wxTradeDetail->BIZ_CHANNELSRV = $merSignInfo->BIZ_CHANNELSRV;
                $wxTradeDetail->TRADENO = $bizContentFormat['trade_no'];
                $wxTradeDetail->TOTALAMOUNT = $bizContentFormat['total_amount'];
                $wxTradeDetail->BODY = $bizContentFormat['body'];
                $wxTradeDetail->CREATETIME = date('Y-m-d H:i:s');
                $wxTradeDetail->NOTIFY_URL = $bizContentFormat['notify_url'];
                $wxTradeDetail->MCH_ATTACH = $bizContentFormat['attach'];
                $wxTradeDetail->PAY_INFO = json_encode($pay_info, JSON_UNESCAPED_UNICODE);
                $wxTradeDetail->INTERFACE_PARAM = $merSignInfo->KEY1;
                $wxTradeDetail->REFUND_STATUS = TradeDetail::REFUND_DEFAULT_STATUS;
                $wxTradeDetail->REFUND_NOTIFY_URL = '';
                $wxTradeDetail->REFUND_NOTIFY_RESPONSE = '';
                $wxTradeDetail->ROLL_SIGNNO = $merSignInfo->ROLL_SIGNNO;
                $wxTradeDetail->OPENID = $bizContentFormat['openid'];
                $wxTradeDetail->save(false);

                //老平安渠道的商户返回的payinfo数据类型不练便，新的格式化成威富通的一样
                if(!in_array($merSignInfo->MERCHANTNO, MerSignInfo::OLD_PINGAN_MERCHANT)){
                    $pay_info = $wxTradeDetail->PAY_INFO;
                }

                $ret->code = GworkerErrorHelper::SRV_CODE_OK;
                $ret->biz_content = array(
                    'attach' => $bizContentFormat['attach'],
                    'trade_no' => $bizContentFormat['trade_no'],
                    'total_amount' => $bizContentFormat['total_amount'],
                    'pay_info' => $pay_info
                );
            }else{
                $ret->code = app()->ErrorHelper->getWxSrvEcode($result['errcode']);
                $ret->message = $result['msg'];
            }

            return $ret->getData();
        });

        $this->addWorkerFunction('pingan.wx.minipay', function($bizContent, $data, $dataOri, $merSignInfo){
            $bizContentFormat = array_merge(array(
                'trade_no' => '',
                'total_amount' => '0',
                'limit_credit_pay' => '0',
                'attach' => '',
                'body' => '微信支付',
                'sub_appid' => '',
                'sub_openid' => '',
                'mch_create_ip' => app()->params['serverIp'],
                'callback_url' => '',
                'type' => '',
                'notify_url' => '',
                'time_start' => null,
                'time_expire' => null
            ), $bizContent);
            $bizContentFormat['trade_no'] = empty($bizContentFormat['trade_no'])? $data['merchant_id'] . time() : $bizContentFormat['trade_no'];
            $bizContentFormat['body'] = empty($bizContentFormat['body'])? '微信支付' : $bizContentFormat['body'];

            $ret = new formatResult($data);
            $unFinishOrder = TradeDetail::model()->findByAttributes(array(
                'TRADENO' => $bizContentFormat['trade_no'],
                'MERCHANTNO' => $merSignInfo->MERCHANTNO,
                'BIZ_TYPE' => TradeDetail::BIZ_TYPE_WX_MINIPAY,
            ));
            if(!empty($unFinishOrder)){
                if(!empty($unFinishOrder->PAY_INFO) && $unFinishOrder->TRADE_STATUS == TradeDetail::TRADE_STATUS_STARTUP){
                    $ret->code = GworkerErrorHelper::SRV_CODE_OK;
                    $ret->biz_content = array(
                        'attach' => $bizContentFormat['attach'],
                        'trade_no' => $bizContentFormat['trade_no'],
                        'total_amount' => $bizContentFormat['total_amount'],
                        'pay_info' => $unFinishOrder->PAY_INFO
                    );

                    return $ret->getData();
                }
            }

            $result = app()->Pingan->api('payorder', array(
                'out_no' => $bizContentFormat['trade_no'],
                'pmt_tag' => $merSignInfo->KEY3,
                'original_amount' => $bizContentFormat['total_amount'] * 100,
                'trade_amount' => $bizContentFormat['total_amount'] * 100,
                'ord_name' => $bizContentFormat['body'],
                'sub_appid' => $bizContentFormat['sub_appid'],
                'sub_openid' => $bizContentFormat['sub_openid'],
                'trade_type' => 'JSAPI',
                'spbill_create_ip' => $bizContentFormat['mch_create_ip'],
                'notify_url' => app()->params['wxpay']['sys_notify_baseurl'].'notify/pinganwxminipay',
            ));
            $result['func'] = 'minipay';

            if($result['errcode'] == 0){
                $wxTradeDetail = TradeDetail::model()->findByAttributes(array(
                    'TRADENO' => $bizContentFormat['trade_no'],
                    'MERCHANTNO' => $merSignInfo->MERCHANTNO,
                    'BIZ_TYPE' => TradeDetail::BIZ_TYPE_WX_MINIPAY
                ));
                if(empty($wxTradeDetail)){
                    $wxTradeDetail = new TradeDetail();
                    $wxTradeDetail->TRADE_STATUS = TradeDetail::TRADE_STATUS_STARTUP;
                }

                $pay_info = json_encode(array(
                    'appId' => $result['data']['appId'],
                    'timeStamp' => $result['data']['timeStamp'],
                    'nonceStr' => $result['data']['nonceStr'],
                    'package' => $result['data']['package'],
                    'signType' => $result['data']['signType'],
                    'paySign' => $result['data']['paySign'],
                ), JSON_UNESCAPED_UNICODE);

                $wxTradeDetail->MERCHANTNO = $data['merchant_id'];
                $wxTradeDetail->TERMINALNO = $data['term_id'];
                $wxTradeDetail->BIZ_CHANNEL = $data['biz_channel'];
                $wxTradeDetail->BIZ_TYPE = $data['biz_type'];
                $wxTradeDetail->CHANNEL_RATE = $merSignInfo->CHANNEL_RATE;
                $wxTradeDetail->CHANNEL_FEE = $merSignInfo->CHANNEL_RATE * 0.01 * $bizContentFormat['total_amount'];
                $wxTradeDetail->BIZ_CHANNELSRV = $merSignInfo->BIZ_CHANNELSRV;
                $wxTradeDetail->TRADENO = $bizContentFormat['trade_no'];
                $wxTradeDetail->TOTALAMOUNT = $bizContentFormat['total_amount'];
                $wxTradeDetail->BODY = $bizContentFormat['body'];
                $wxTradeDetail->CREATETIME = date('Y-m-d H:i:s');
                $wxTradeDetail->NOTIFY_URL = $bizContentFormat['notify_url'];
                $wxTradeDetail->MCH_ATTACH = $bizContentFormat['attach'];
                $wxTradeDetail->PAY_INFO = $pay_info;
                $wxTradeDetail->INTERFACE_PARAM = $merSignInfo->KEY1;
                $wxTradeDetail->REFUND_STATUS = TradeDetail::REFUND_DEFAULT_STATUS;
                $wxTradeDetail->REFUND_NOTIFY_URL = '';
                $wxTradeDetail->REFUND_NOTIFY_RESPONSE = '';
                $wxTradeDetail->ROLL_SIGNNO = $merSignInfo->ROLL_SIGNNO;
                $wxTradeDetail->OPENID = $bizContentFormat['sub_openid'];
                $wxTradeDetail->save(false);

                $ret->code = GworkerErrorHelper::SRV_CODE_OK;
                $ret->biz_content = array(
                    'attach' => $bizContentFormat['attach'],
                    'trade_no' => $bizContentFormat['trade_no'],
                    'total_amount' => $bizContentFormat['total_amount'],
                    'pay_info' => $pay_info
                );
            }else{
                $ret->code = app()->ErrorHelper->getWxSrvEcode($result['errcode']);
                $ret->message = $result['msg'];
            }

            return $ret->getData();
        });

        $this->addWorkerFunction('pingan.wx.query', function($bizContent, $data, $dataOri, $merSignInfo){
            $bizContentFormat = array_merge(array(
                'trade_no' => '',
                'wx_trade_no' => '',
            ), $bizContent);

            $result = app()->Pingan->api('order/view', array(
                'out_no' => $bizContentFormat['trade_no'],
            ));
            $result['func'] = 'query';

            $ret = new formatResult($data);
            if($result['errcode'] == 0){
                $ret->code = GworkerErrorHelper::SRV_CODE_OK;
                $ret->biz_content = array(
                    'trade_no' => $result['data']['out_no'],
                    'wx_trade_no' => $result['data']['trade_no'],
                    'trade_status' => TradeDetail::GetTradeStatus($result['data']['status']),
                    'total_amount' => round($result['data']['trade_amount'] / 100.00, 2),
                    'coupon_fee' => '0',
                    'send_pay_time' => $result['data']['trade_time']
                );
            }else{
                $ret->code = app()->ErrorHelper->getWxSrvEcode($result['errcode']);
                $ret->message = $result['msg'];
            }

            return $ret->getData();
        });

        $this->addWorkerFunction('pingan.wx.refund', function($bizContent, $data, $dataOri, $merSignInfo){
            $bizContentFormat = array_merge(array(
                'trade_no' => '',
                'wx_trade_no' => '',
                'refund_amount' => '',
                'out_refund_no' => '',
                'notify_url' => '',
            ), $bizContent);

            if(!empty($bizContent['wx_trade_no'])){
                $wxTradeDetail = TradeDetail::model()->findByAttributes(array(
                    'MERCHANTNO' => $merSignInfo->MERCHANTNO,
                    'CHANNEL_TRADENO' => $bizContentFormat['wx_trade_no'],
                    'BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
                    'BIZ_TYPE' => array(
                        TradeDetail::BIZ_TYPE_WX_PREPAY,
                        TradeDetail::BIZ_TYPE_WX_JSPAY,
                        TradeDetail::BIZ_TYPE_WX_MICROPAY,
                        TradeDetail::BIZ_TYPE_WX_APPPAY,
                    )
                ));
            }else if(!empty($bizContent['trade_no'])){
                $wxTradeDetail = TradeDetail::model()->findByAttributes(array(
                    'MERCHANTNO' => $merSignInfo->MERCHANTNO,
                    'TRADENO' => $bizContentFormat['trade_no'],
                    'BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
                    'BIZ_TYPE' => array(
                        TradeDetail::BIZ_TYPE_WX_PREPAY,
                        TradeDetail::BIZ_TYPE_WX_JSPAY,
                        TradeDetail::BIZ_TYPE_WX_MICROPAY,
                        TradeDetail::BIZ_TYPE_WX_APPPAY,
                    )
                ));
            }

            $ret = new formatResult($data);
            if(!empty($wxTradeDetail)){
                if($wxTradeDetail->TRADE_STATUS == TradeDetail::TRADE_STATUS_CANCELED){
                    $ret->code = app()->ErrorHelper->getWxSrvEcode('TRADE_STATE_ERROR');
                    $ret->message = app()->ErrorHelper->getMsg($ret->code);
                    return $ret->getData();
                }

                $refundedAmout = app()->db->createCommand('
                    SELECT SUM(REFUNDFEE) REFUNDFEE_SUM FROM TRADEDETAIL
                    WHERE MERCHANTNO = :MERCHANTNO AND TRADENO = :TRADENO AND BIZ_CHANNEL = :BIZ_CHANNEL AND BIZ_TYPE = :BIZ_TYPE
                ')->queryScalar(array(
                    ':MERCHANTNO' => $merSignInfo->MERCHANTNO,
                    ':TRADENO' => $wxTradeDetail->TRADENO,
                    ':BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
                    ':BIZ_TYPE' => TradeDetail::BIZ_TYPE_WX_REFUND
                ));
                if(
                    bccomp($bizContentFormat['refund_amount'], $wxTradeDetail->TOTALAMOUNT, 2) === 1 ||
                    bccomp($refundedAmout, $wxTradeDetail->TOTALAMOUNT, 2) != -1 ||
                    ($refundedAmout > 0 && (bccomp($refundedAmout + $bizContentFormat['refund_amount'], $wxTradeDetail->TOTALAMOUNT, 2) === 1))
                ){
                    $ret->code = app()->ErrorHelper->getWxSrvEcode('REFUND_FEE_INVALID');
                    $ret->message = app()->ErrorHelper->getMsg($ret->code);
                    return $ret->getData();
                }

                $result = app()->Pingan->api('payrefund', array(
                    'out_no' => $bizContentFormat['trade_no'],
                    'refund_out_no' => $bizContentFormat['out_refund_no'],
                    'refund_ord_name' => '退款',
                    'refund_amount' => $bizContentFormat['refund_amount'] * 100,
                    'shop_pass' => sha1("123456"),
                    'sign_type' => 'RSA'
                ));
                $result['func'] = 'refund';

                if($result['errcode'] == 0){
                    $wxRefundDetail = new TradeDetail();
                    $wxRefundDetail->BIZ_CHANNEL = TradeDetail::BIZ_CHANNEL_WXPAY;
                    $wxRefundDetail->BIZ_TYPE = TradeDetail::BIZ_TYPE_WX_REFUND;
                    $wxRefundDetail->MERCHANTNO = $data['merchant_id'];
                    $wxRefundDetail->TERMINALNO = $data['term_id'];
                    $wxRefundDetail->TRADE_STATUS = TradeDetail::TRADE_STATUS_SUCCESS;
                    $wxRefundDetail->TRADENO = $wxTradeDetail->TRADENO;
                    $wxRefundDetail->REFUNDNO = $bizContentFormat['out_refund_no'];
                    $wxRefundDetail->REFUNDID = $result['data']['ord_no'];
                    $wxRefundDetail->CHANNEL_TRADENO = $wxTradeDetail->CHANNEL_TRADENO;
                    $wxRefundDetail->BUYERID = $wxTradeDetail->BUYERID;
                    $wxRefundDetail->CHANNEL_RATE = $merSignInfo->CHANNEL_RATE;
                    $wxRefundDetail->BIZ_CHANNELSRV = $merSignInfo->BIZ_CHANNELSRV;
                    $wxRefundDetail->CHANNEL_FEE = round($bizContentFormat['refund_amount'] * $merSignInfo->CHANNEL_RATE / 100.00, 2);
                    $wxRefundDetail->REFUNDFEE = $bizContentFormat['refund_amount'];
                    $wxRefundDetail->SUBJECT = '';
                    $wxRefundDetail->BODY = $wxTradeDetail->BODY;
                    $wxRefundDetail->CREATETIME = date('Y-m-d H:i:s');
                    $wxRefundDetail->INTERFACE_PARAM = $merSignInfo->KEY1;
                    $wxRefundDetail->REFUND_NOTIFY_URL = $bizContentFormat['notify_url'];
                    $wxRefundDetail->REFUND_STATUS = TradeDetail::REFUND_DEFAULT_STATUS;
                    $wxRefundDetail->ROLL_SIGNNO = $merSignInfo->ROLL_SIGNNO;
                    $wxRefundDetail->REFUND_NOTIFY_RESPONSE = '';
                    $wxRefundDetail->save(false);

                    if(bccomp($refundedAmout + $bizContentFormat['refund_amount'], $wxTradeDetail->TOTALAMOUNT, 2) != -1){
                        $wxTradeDetail->TRADE_STATUS = TradeDetail::TRADE_STATUS_REFUND;
                        $wxTradeDetail->save(false);
                    }

                    $ret->code = GworkerErrorHelper::SRV_CODE_OK;
                    $ret->biz_content = array(
                        'trade_no' => $result['data']['out_no'],
                        'wx_trade_no' => $wxTradeDetail->CHANNEL_TRADENO,
                        'refund_id' => $result['data']['ord_no'],
                        'total_amount' => $wxTradeDetail->TOTALAMOUNT * 1.00,
                        'coupon_fee' => '0',
                        'refund_channel' => 'ORIGINAL'
                    );
                }else{
                    $ret->code = app()->ErrorHelper->getWxSrvEcode($result['errcode']);
                    $ret->message = $result['msg'];
                }

                return $ret->getData();
            }else{
                $ret->code = app()->ErrorHelper->getWxSrvEcode('ORDERNOTEXIST');
                $ret->message = app()->ErrorHelper->getMsg($ret->code);
            }

            return $ret->getData();
        });

        $this->addWorkerFunction('pingan.wx.refundquery', function($bizContent, $data, $dataOri, $merSignInfo){
            $bizContentFormat = array_merge(array(
                'trade_no' => '',
                'refund_id' => '',
                'out_refund_no' => ''
            ), $bizContent);

            $ret = new formatResult($data);

            if(!empty($bizContentFormat['out_refund_no'])){
                $wxRefundDetail = TradeDetail::model()->findByAttributes(array(
                    'BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
                    'TRADENO' => $bizContentFormat['trade_no'],
                    'MERCHANTNO' => $merSignInfo->MERCHANTNO,
                    'REFUNDNO' => $bizContentFormat['out_refund_no']
                ));
            }else if(!empty($bizContentFormat['refund_id'])){
                $wxRefundDetail = TradeDetail::model()->findByAttributes(array(
                    'BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
                    'TRADENO' => $bizContentFormat['trade_no'],
                    'MERCHANTNO' => $merSignInfo->MERCHANTNO,
                    'REFUNDID' => $bizContentFormat['refund_id'],
                ));
            }
            if(empty($wxRefundDetail)){
                $ret->code = app()->ErrorHelper->getWxSrvEcode('ORDERNOTEXIST');
                $ret->message = app()->ErrorHelper->getMsg($ret->code);
                return $ret->getData();
            }

            $result = app()->Pingan->api('payrefundquery', array(
                'refund_out_no' => $wxRefundDetail->REFUNDNO,
                'refund_ord_no' => $wxRefundDetail->REFUNDID,
            ));
            $result['func'] = 'payrefundquery';

            $ret = new formatResult($data);
            if($result['errcode'] == 0){
                $ret->code = GworkerErrorHelper::SRV_CODE_OK;
                $ret->biz_content = $result['data'];
            }else{
                $ret->code = app()->ErrorHelper->getWxSrvEcode($result['errcode']);
                $ret->message = $result['msg'];
            }

            return $ret->getData();
        });

		/****************************** Notify Workers ***************************/
        $this->_worker->addFunction('pingan.wx.jspaynotify', function($job){
				echo 'Jspaynotify is called at ' . date('Y-m-d H:i:s') . "\n";
			
				$workLoadArgs = json_decode($job->workload(), true);
				$postData = $workLoadArgs['postData'];
				
				if(true){		// TODO: verify signature
					$merSign = MerSignInfo::model()->findByAttributes(array(
				        'BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
                        'BIZ_CHANNELSRV' => MerSignInfo::BIZ_CHANNELSRV_PINGAN,
					    'KEY1' => $postData['open_id']
					));
					
					if($merSign){
					    $wxTradeDetail = TradeDetail::model()->findByAttributes(array(
					        'MERCHANTNO' => $merSign->MERCHANTNO, 
					        'TRADENO' => $postData['out_no'],
					        'BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
					        'BIZ_TYPE' => TradeDetail::BIZ_TYPE_WX_JSPAY,
					        'BIZ_CHANNELSRV' => MerSignInfo::BIZ_CHANNELSRV_PINGAN,
					    ));
					    
					    if(empty($wxTradeDetail)){
					        lg(array(
					            'func' => 'pingan.wx.jspaynotify', 
					            'workloaded' => $job->workload(),
					            'response' => false
					        ));
					        return json_encode(array(
					            'ret' => false,
					        ));
					    }
					     
					    $sysResult = false;
					    $mchNotiResult = false;
					    if( in_array($wxTradeDetail->TRADE_STATUS, array(TradeDetail::TRADE_STATUS_SUCCESS, TradeDetail::TRADE_STATUS_REFUND, TradeDetail::TRADE_STATUS_CANCELED)) ){
					        $sysResult = true;
					    }else{
					        $tradeResult = json_decode($postData['trade_result'], true);
					        $time_end = empty($tradeResult['time_end'])? '' : (substr($tradeResult['time_end'], 0, 4) . '-' . substr($tradeResult['time_end'], 4, 2) . '-' . substr($tradeResult['time_end'], 6, 2) . ' ' . substr($tradeResult['time_end'], 8, 2) . ':' . substr($tradeResult['time_end'], 10, 2) . ':' . substr($tradeResult['time_end'], 12, 2));
					        
					        $wxTradeDetail->CHANNEL_TRADENO = $tradeResult['transaction_id'];
					        $wxTradeDetail->TRADE_STATUS = TradeDetail::GetTradeStatusCode($tradeResult['return_code']);
					        $wxTradeDetail->TOTALAMOUNT = round($tradeResult['total_fee'] / 100.00, 2);
					        $wxTradeDetail->PAYMENTTIME = $time_end;
					        $wxTradeDetail->EXCHANGE_RATE = TradeDetail::getCurrentRate();

					        $sysResult = $wxTradeDetail->save(false);
					    }
					     
					    if($sysResult){
					        $mchNotiResult = true;
					        if(!empty($wxTradeDetail->NOTIFY_URL)){
					            $mchNotiResult = false;
					    
					            $merBaseInfo = MerchantBaseInfo::model()->findByAttributes(array(
					                'MERCHANTNO' => $wxTradeDetail->MERCHANTNO,
					            ));
					    
					            if($merBaseInfo){
					                $postNotiData = $postData;
					                unset($postNotiData['SignType']);
					                unset($postNotiData['SignMsg']);
					                $postNotiData['attach'] = $wxTradeDetail->MCH_ATTACH;
					                $postNotiData = $this->_signNotiPinganMch('md5', json_encode($postNotiData), $merBaseInfo->TOKEN_MD5);
					                 
					                $notiRet = app()->Pingan->Notify($wxTradeDetail->NOTIFY_URL, $postNotiData);
					                if($notiRet){
					                    $wxTradeDetail->NOTIFY_URL = '';
					                    $wxTradeDetail->save(false);
					    
					                    $mchNotiResult = true;
					                }
					            }
					        }
					    }
					     
					    lg(array(
					        'workloaded' => $job->workload(),
					        'response' => $mchNotiResult && $sysResult
					    ));
					    return json_encode(array(
					        'ret' => $mchNotiResult && $sysResult,
					    ));
					}
				}
			});

        $this->_worker->addFunction('pingan.wx.minipaynotify', function($job){
            echo 'Jspaynotify is called at ' . date('Y-m-d H:i:s') . "\n";

            $workLoadArgs = json_decode($job->workload(), true);
            $postData = $workLoadArgs['postData'];

            if(true){		// TODO: verify signature
                $merSign = MerSignInfo::model()->findByAttributes(array(
                    'BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
                    'BIZ_CHANNELSRV' => MerSignInfo::BIZ_CHANNELSRV_PINGAN,
                    'KEY1' => $postData['open_id']
                ));

                if($merSign){
                    $wxTradeDetail = TradeDetail::model()->findByAttributes(array(
                        'MERCHANTNO' => $merSign->MERCHANTNO,
                        'TRADENO' => $postData['out_no'],
                        'BIZ_CHANNEL' => TradeDetail::BIZ_CHANNEL_WXPAY,
                        'BIZ_TYPE' => TradeDetail::BIZ_TYPE_WX_MINIPAY,
                        'BIZ_CHANNELSRV' => MerSignInfo::BIZ_CHANNELSRV_PINGAN,
                    ));

                    if(empty($wxTradeDetail)){
                        lg(array(
                            'func' => 'pingan.wx.jspaynotify',
                            'workloaded' => $job->workload(),
                            'response' => false
                        ));
                        return json_encode(array(
                            'ret' => false,
                        ));
                    }

                    $sysResult = false;
                    $mchNotiResult = false;
                    if( in_array($wxTradeDetail->TRADE_STATUS, array(TradeDetail::TRADE_STATUS_SUCCESS, TradeDetail::TRADE_STATUS_REFUND, TradeDetail::TRADE_STATUS_CANCELED)) ){
                        $sysResult = true;
                    }else{
                        $tradeResult = json_decode($postData['trade_result'], true);
                        $time_end = empty($tradeResult['time_end'])? '' : (substr($tradeResult['time_end'], 0, 4) . '-' . substr($tradeResult['time_end'], 4, 2) . '-' . substr($tradeResult['time_end'], 6, 2) . ' ' . substr($tradeResult['time_end'], 8, 2) . ':' . substr($tradeResult['time_end'], 10, 2) . ':' . substr($tradeResult['time_end'], 12, 2));

                        $wxTradeDetail->CHANNEL_TRADENO = $tradeResult['transaction_id'];
                        $wxTradeDetail->TRADE_STATUS = TradeDetail::GetTradeStatusCode($tradeResult['return_code']);
                        $wxTradeDetail->TOTALAMOUNT = round($tradeResult['total_fee'] / 100.00, 2);
                        $wxTradeDetail->PAYMENTTIME = $time_end;
                        $wxTradeDetail->EXCHANGE_RATE = TradeDetail::getCurrentRate();

                        $sysResult = $wxTradeDetail->save(false);
                    }

                    if($sysResult){
                        $mchNotiResult = true;
                        if(!empty($wxTradeDetail->NOTIFY_URL)){
                            $mchNotiResult = false;

                            $merBaseInfo = MerchantBaseInfo::model()->findByAttributes(array(
                                'MERCHANTNO' => $wxTradeDetail->MERCHANTNO,
                            ));

                            if($merBaseInfo){
                                $postNotiData = $postData;
                                unset($postNotiData['SignType']);
                                unset($postNotiData['SignMsg']);
                                $postNotiData['attach'] = $wxTradeDetail->MCH_ATTACH;
                                $postNotiData = $this->_signNotiPinganMch('md5', json_encode($postNotiData), $merBaseInfo->TOKEN_MD5);

                                $notiRet = app()->Pingan->Notify($wxTradeDetail->NOTIFY_URL, $postNotiData);
                                if($notiRet){
                                    $wxTradeDetail->NOTIFY_URL = '';
                                    $wxTradeDetail->save(false);

                                    $mchNotiResult = true;
                                }
                            }
                        }
                    }

                    lg(array(
                        'workloaded' => $job->workload(),
                        'response' => $mchNotiResult && $sysResult
                    ));
                    return json_encode(array(
                        'ret' => $mchNotiResult && $sysResult,
                    ));
                }
            }
        });
		echo "WxpayPingan Gworker is started\n";
		while ($this->_worker->work());
	}
}
